#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"

//V2版本：
//功能：小写字符转大写、远程 bash
//核心：业务处理模块化，实际处理函数与服务端解耦

namespace ns_server
{
  const static uint16_t default_port = 8080; // 默认端口号
  using func_t = std::function<std::string(std::string)>;

  class UdpServer
  {
  public:
    UdpServer(func_t service, uint16_t port = default_port)
        : port_(port)
        ,service_(service)
    {
    }

    ~UdpServer()
    {
    }

    void StartServer()
    {
      // 1、创建套接字
      sock_ = socket(AF_INET, SOCK_DGRAM, 0); // IPV4 标准的网络通信
      if (sock_ == -1)
      {
        std::cerr << "Create Socket Fail: " << strerror(errno) << std::endl;
        exit(SOCKET_ERR);
      }
      std::cout << "Create Socket Succese: " << sock_ << std::endl;

      // 2、绑定 IP地址 和 Port号
      struct sockaddr_in local;
      bzero(&local, sizeof(local));

      local.sin_family = AF_INET;    // PF_INET 也行
      local.sin_port = htons(port_); // 设置端口号(主机转网络)
      // local.sin_addr.s_addr = inet_addr(ip_.c_str()); //点分十进制字符串转为整型，整型序列转为网络序列
      local.sin_addr.s_addr = INADDR_ANY; // 绑定任何可用的 IP 地址

      if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
      {
        std::cerr << "Bind Socket Fail: " << strerror(errno) << std::endl;
        exit(BIND_ERR);
      }
      std::cout << "Bind Socket Succese: " << sock_ << std::endl;

      std::cout << inet_ntoa(local.sin_addr) << " : " << ntohs(local.sin_port) << std::endl;
    }

    void Recv()
    {
      char buffer[1024] = {0}; // 缓冲区大小

      while (true)
      {
        // 收信息
        struct sockaddr_in peer; // 客户端的套接字信息
        socklen_t len = sizeof peer;
        int n = recvfrom(sock_, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&peer, &len);
        if(n > 0) buffer[n] = '\0';
        else continue;

        //提取客户端的信息
        std::string clientip = inet_ntoa(peer.sin_addr);  //ip
        uint16_t clientport = ntohs(peer.sin_port); //port
        std::cout << "client [" << clientip << ":" << clientport << "] $ " << buffer << std::endl;

        //业务处理
        std::string sendMsg =  service_(buffer);

        // 发信息
        sendto(sock_, sendMsg.c_str(), sendMsg.size(), 0,  (struct sockaddr*)&peer, len);  //将数据回响给客户端
      }
    }

  private:
    int sock_;      // 套接字
    uint16_t port_; // 端口号
    // std::string ip_;  //IP地址，可以不指定
    func_t service_;  //提供业务处理的函数
  };
}